package cz.cvut.manatee.processManager;

import java.util.concurrent.Semaphore;
import java.util.LinkedList;


/*
 * Prideluje praci (dotazy) jednotlivym vlaknum
 */
public class Assigner implements Runnable  {
	private final QueryQueue queue;
	private final LinkedList<ThreadListItem> seznamVlaken;
	
	/*
	 * Semafory pro RMI a volna vlakna.
	 */
	protected final Semaphore semWaiting;
	protected final Semaphore semThread;

	/*
	 * pripocteni score pri shode s CQL
	 */
	protected static int ageMultiplyCoef = 2; 
	
	
	Assigner(QueryQueue queue, LinkedList<ThreadListItem> seznamVlaken) {
		this.queue        = queue;
		this.seznamVlaken = seznamVlaken;
		
		this.semWaiting = new Semaphore(0);
		this.semThread = new Semaphore(0);
		
		
	}
	
	
	public void semThreadRelease(){
		this.semThread.release();
	}

	public void semWaitingRelease(){
		this.semWaiting.release();
	}

	public void addQueryToQueue(Query q) throws AddingEmptyQueryException, AddingNotWaitingQueryException{
		this.queue.add(q);
		this.semWaitingRelease();
	}

	public void removeQueryFromQueue(Query q) throws RemovingNotFinishedQueryFromQueueException{
		this.queue.remove(q);
	}
	
	public void removeQueryFromQueueForce(Query q) {
		this.queue.removeForce(q);
	}
	


	protected ThreadRunnable findBestThread(Query query) {
		int maxScore = -1;
		int score    =  0;
		Query q      = null;
		ThreadRunnable bestThread = null;
		
		synchronized (this.seznamVlaken) {
			for (ThreadListItem item : this.seznamVlaken) {
				ThreadRunnable thread = item.getRunnable();
				if (thread.isAliveAndNotBusy()) {
					q      = thread.getQuery();
					score  = query.compare(q)+thread.getAge()*Assigner.ageMultiplyCoef;
					
					if ( score > maxScore ) {
						maxScore   = score;
						bestThread = thread;
					}
					
					thread.incrementAge();
				}
			}
		}
		
		return bestThread;
	}

	protected ThreadRunnable findBestThread_old(Query query) {
		int maxScore = -1;
		int maxAge   = -1;
		int score    =  0;
		int age      =  0;
		Query q      = null;
		ThreadRunnable bestThread = null;
		
		for (ThreadListItem item : this.seznamVlaken) {
			ThreadRunnable thread = item.getRunnable();
			if (thread.isAliveAndNotBusy()) {
				q      = thread.getQuery();
				score  = query.compare(q);
				age    = thread.getAge();
				
				if ( (score > maxScore) || ( (score == maxScore) && (age > maxAge) ) ) {
					maxScore   = score;
					maxAge     = age;
					bestThread = thread;
				}
				
				thread.incrementAge();
			}
		}
		
		return bestThread;
	}
	
	
	
	public void run () {
		try{
			while(true){
				//snizeni semaforu
				
				Logging.get().info("Assigner: chce snizit semafory");
				this.semThread.acquire();
				this.semWaiting.acquire();
				Logging.get().info("Assigner: prideluji dotaz");
				
				Query lastWaitingQuery = this.queue.getLastWaitingQuery();
				ThreadRunnable bestThread = this.findBestThread(lastWaitingQuery);
				
				if(bestThread != null && lastWaitingQuery != null) {
					try {
						bestThread.setQuery(lastWaitingQuery);
					} catch (ProcessManagerException e) {
						System.out.println(e);
					}
					
				}
				
			}
		}
		catch(InterruptedException ie){
			
		}

		Logging.get().info("Assigner: ukoncuji se "+this);
	}

}
